/*
 * commons is a based project implemented
 * Copyright (C) 2024 Jasmine
 */
package com.commons.core.utils;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

import org.springframework.cglib.beans.BeanCopier;

/**
 * bean转换工具类
 *
 * @author Jasmine
 * @version 1.0
 * @date 2021/04/02 10:14
 * @see BeanCopyUtil
 * @since JDK1.8
 */
public final class BeanCopyUtil {

    /**
     * BeanCopier的缓存
     */
    static final ConcurrentHashMap<String, BeanCopier> BEAN_COPIER_CACHE = new ConcurrentHashMap<>();

    /**
     * 单个对象属性拷贝
     *
     * @param source 源对象
     * @param clazz 目标对象Class
     * @param <T> 目标对象类型
     * @param <M> 源对象类型
     * @return 目标对象
     */
    public static <T, M> T copyProperty(M source, Class<T> clazz) {
        if (Objects.isNull(source) || Objects.isNull(clazz)) {
            throw new IllegalArgumentException();
        }
        String key = genKey(source.getClass(), clazz);

        BeanCopier beanCopier;
        if (BEAN_COPIER_CACHE.containsKey(key)) {
            beanCopier = BEAN_COPIER_CACHE.get(key);
        } else {
            beanCopier = BeanCopier.create(source.getClass(), clazz, false);
            BEAN_COPIER_CACHE.put(key, beanCopier);
        }

        Object t = null;
        try {
            // 利用Constructor.newInstance()反射无参构造方法
            Constructor constructor = clazz.getDeclaredConstructor();
            t = constructor.newInstance();
            beanCopier.copy(source, t, null);
        } catch (InstantiationException | IllegalAccessException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        return (T)t;
    }

    /**
     * 单个对象属性拷贝
     *
     * @param source 源对象
     * @param target 目标对象
     * @return 目标对象
     */
    public static void copyProperty(Object source, Object target) {
        String key = genKey(source.getClass(), target.getClass());
        BeanCopier beanCopier;
        if (BEAN_COPIER_CACHE.containsKey(key)) {
            beanCopier = BEAN_COPIER_CACHE.get(key);
        } else {
            beanCopier = BeanCopier.create(source.getClass(), target.getClass(), false);
            BEAN_COPIER_CACHE.put(key, beanCopier);
        }
        beanCopier.copy(source, target, null);
    }

    /**
     * 列表对象拷贝
     *
     * @param sourceList 源列表
     * @param clazz 源列表对象Class
     * @param <T> 目标列表对象类型
     * @param <M> 源列表对象类型
     * @return 目标列表
     */
    public static <T, M> List<T> copyListProperties(List<M> sourceList, Class<T> clazz) {
        return Optional.of(sourceList).orElse(new ArrayList<>()).stream().map(m -> copyProperty(m, clazz))
            .collect(Collectors.toList());
    }

    /**
     * 生成key
     *
     * @param srcClazz 源文件的class
     * @param tgtClazz 目标文件的class
     * @return string
     */
    private static String genKey(Class<?> srcClazz, Class<?> tgtClazz) {
        return srcClazz.getName() + ":" + tgtClazz.getName();
    }
}
